#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:zhengxin
@file: 03_PandasDataFrame.py
@time: 2025/1/2  10:17
# @describe:
"""
"""
Pandas 数据结构 - DataFrame
    DataFrame 是 Pandas 中的另一个核心数据结构，类似于一个二维的表格或数据库中的数据表。
    DataFrame 是一个表格型的数据结构，它含有一组有序的列，每列可以是不同的值类型（数值、字符串、布尔型值）。
    DataFrame 既有行索引也有列索引，它可以被看做由 Series 组成的字典（共同用一个索引）。
    DataFrame 提供了各种功能来进行数据访问、筛选、分割、合并、重塑、聚合以及转换等操作。
    DataFrame 是一个非常灵活且强大的数据结构，广泛用于数据分析、清洗、转换、可视化等任务。
    
    DataFrame 特点：
    二维结构： DataFrame 是一个二维表格，可以被看作是一个 Excel 电子表格或 SQL 表，具有行和列。可以将其视为多个 Series 对象组成的字典。
    列的数据类型： 不同的列可以包含不同的数据类型，例如整数、浮点数、字符串或 Python 对象等。
    索引：DataFrame 可以拥有行索引和列索引，类似于 Excel 中的行号和列标。
    大小可变：可以添加和删除列，类似于 Python 中的字典。
    自动对齐：在进行算术运算或数据对齐操作时，DataFrame 会自动对齐索引。
    处理缺失数据：DataFrame 可以包含缺失数据，Pandas 使用 NaN（Not a Number）来表示。
    数据操作：支持数据切片、索引、子集分割等操作。
    时间序列支持：DataFrame 对时间序列数据有特别的支持，可以轻松地进行时间数据的切片、索引和操作。
    
    丰富的数据访问功能：通过 .loc、.iloc 和 .query() 方法，可以灵活地访问和筛选数据。
        
        灵活的数据处理功能：包括数据合并、重塑、透视、分组和聚合等。
        数据可视化：虽然 DataFrame 本身不是可视化工具，但它可以与 Matplotlib 或 Seaborn 等可视化库结合使用，进行数据可视化。
        高效的数据输入输出：可以方便地读取和写入数据，支持多种格式，如 CSV、Excel、SQL 数据库和 HDF5 格式。
        描述性统计：提供了一系列方法来计算描述性统计数据，如 .describe()、.mean()、.sum() 等。
        灵活的数据对齐和集成：可以轻松地与其他 DataFrame 或 Series 对象进行合并、连接或更新操作。
        转换功能：可以对数据集中的值进行转换，例如使用 .apply() 方法应用自定义函数。
        滚动窗口和时间序列分析：支持对数据集进行滚动窗口统计和时间序列分析。
"""


"""
DataFrame 构造方法如下：
    pandas.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
    
参数说明：
    
    data：DataFrame 的数据部分，可以是字典、二维数组、Series、DataFrame 或其他可转换为 DataFrame 的对象。如果不提供此参数，则创建一个空的 DataFrame。
    index：DataFrame 的行索引，用于标识每行数据。可以是列表、数组、索引对象等。如果不提供此参数，则创建一个默认的整数索引。
    columns：DataFrame 的列索引，用于标识每列数据。可以是列表、数组、索引对象等。如果不提供此参数，则创建一个默认的整数索引。
    dtype：指定 DataFrame 的数据类型。可以是 NumPy 的数据类型，例如 np.int64、np.float64 等。如果不提供此参数，则根据数据自动推断数据类型。
    copy：是否复制数据。默认为 False，表示不复制数据。如果设置为 True，则复制输入的数据。
"""

"""使用列表创建"""
import pandas as pd
import numpy as np

data = [['Google', 10], ['Runoob', 12], ['Wiki', 13]]
# 创建 dataFrame
df = pd.DataFrame(data, columns=['Site', 'Age'])
print(df)

# 使用 astype 方法，设置每列的数据类型
df['Site'] = df['Site'].astype(str)
df["Age"] = df['Age'].astype(float)
print(df)


"""使用字典创建"""

data = {'Site':['Google', 'Runoob', 'Wiki'], 'Age':[10, 12, 13]}
df = pd.DataFrame(data)
print(df)


""" 使用 ndarrays 创建 """
# 创建一个包含网站和年龄的二维 ndarray
ndarray_data = np.array([
    ['Google', 10],
    ['Runoob', 12],
    ['Wiki', 13]
])

# 使用 DataFrame 构造函数，创建数据帧
df = pd.DataFrame(ndarray_data, columns=['Site2', 'Age2'])
print(df)



""" 使用字典（key/value），其中字典的 key 为列名: """
data = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
df = pd.DataFrame(data)
print("使用字典（key/value）:", df)


""" Pandas 可以使用 loc 属性返回指定行的数据，如果没有设置索引，第一行索引为 0，第二行索引为 1，以此类推： """
data = {
    "calories": [420, 380, 390],
    "duration": [50, 40, 45]
}
# 数据加载到 DataFrame 对象
df = pd.DataFrame(data)

# 返回第一行
print("返回第一行:", df.loc[0])
# 返回第二行
print("返回第二行:", df.loc[1])
# 返回多行数据，使用 [[ ... ]] 格式，... 为各行的索引，以逗号隔开：
print("返回多行数据:", df.loc[[0, 1, 2]])


# 指定索引值
df = pd.DataFrame(data, index=["day1", "day2", "day3"])
print("指定索引值:", df)

# Pandas 可以使用 loc 属性返回指定索引对应到某一行：
print("指定索引对应到某一行:", df.loc["day2"])






""" DataFrame 方法 """
import pandas as pd

# 创建 DataFrame
data = {
    "Name": ["Alice", "Bob", "Charlie", "David"],
    "Age": [25, 30, 35, 40],
    'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']
}
df = pd.DataFrame(data)
print('-'* 30)


# 查看前两行数据
print("查看前两行数据: ",'\n',  df.head(2))
print('-'* 30)

# 查看DataFrame 的基本信息
print(df.info())
print('-'* 30)

# 获取描述统计信息
print(df.describe())
print('-'* 30)

# 按年龄排序
df_sorted = df.sort_values(by='Age', ascending=False)
print(df_sorted)
print('-'* 30)

# 选择指定列
print(df[["Name", "Age"]])
print('-'* 30)


# 按索引选择行
print(df.iloc[1:3])     # 选择第二到第三行(按位置)
print('-'* 30)

# 按标签选择行
print(df.loc[1:2])      # 选择第二到第三行(按标签)
print('-'* 30)

# 计算分组统计(按城市分组，计算平均年龄)
print(df.groupby("City")["Age"].mean())

# 处理缺失值(填充缺失值)
df["Age"] = df["Age"].fillna(31)

# 导出为 CSV文件
df.to_csv("output.csv", index=False)




"""
    更多 DataFrame 说明
    创建 DataFrame
"""
# 从字典创建：字典的键成为列名，值成为列数据。
import pandas as pd

df = pd.DataFrame({"Column1": [1, 2, 3], "Column2": [4, 5, 6]})

# 从列表的列表创建：外层列表代表行，内层列表代表列。
df = pd.DataFrame([[1, 2, 3], [4, 5, 6], [7, 8, 9]],
                  columns=["Column1", "Column2", "Column3"])

# 从 NumPy 数组创建：提供一个二维 NumPy 数组。
df = pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))
print(df)
print('-'* 30)

# 从 Series 创建 DataFrame：通过 pd.Series() 创建。
s1 = pd.Series(['Alice', 'Bob', 'Charlie'])
s2 = pd.Series([25, 30, 35])
s3 = pd.Series(['New York', 'Los Angeles', 'Chicago'])
df = pd.DataFrame({'Name': s1, 'Age': s2, 'City': s3})
print(df)
print('-'* 30)





"""  DataFrame 的属性和方法  """
print(df.shape)         # 形状
print(df.columns)       # 列名
print(df.index)         # 索引
print(df.head(5))       # 前几行数据，默认是前 5行
print(df.tail(5))       # 后几行数据，默认是后 5行
print(df.info())        # 数据信息
print(df.describe())    # 描述统计信息
print(df["Age"].mean())     # 求平均值
print(df["Age"].sum())         # 求和



""" 
访问 DataFrame 元素
    访问列：使用列名作为属性或通过 .loc[]、.iloc[] 访问，也可以使用标签或位置索引。。 
"""
print(df)
# 通过列名访问
print(df["Name"])
print('-'* 30)


# 通过属性访问
print(df.Age)

# 通过 .loc[]访问
print(df.loc[:, "Name"])

# 通过 .iloc[] 访问
print(df.iloc[:, 0])    # 假设 'Name' 是第一列

# 访问单个元素
print(df["Name"][0])

# 访问行：使用行的标签和 .loc[] 访问。
print(df.loc[0, 'Age'])
print('-'* 30)







""" 修改 DataFrame  """
# 修改列数据：直接对列进行赋值。
df["Age"] = [10, 11, 12]
print(df)

# 添加新列：给新列赋值。
df["NewColumn"] = [100, 200, 300]
print(df)

# 添加新行：使用 loc、append 或 concat 方法。
df.loc[3] = [13, 14, 15, 16]
print(df)
print('-'* 30)


# 使用 concat 添加新行到末尾
new_row = pd.DataFrame([[4, 7]], columns=['A', 'B'])  # 创建一个只包含新行的DataFrame
df = pd.concat([df, new_row], ignore_index=True)  # 将新行添加到原始DataFrame
print(df)
print('-'* 30)








""" 删除 DataFrame 元素  """
# 删除列：使用 drop 方法
df_dropped = df.drop("B", axis=1)
print(df_dropped)
print('-'* 30)


# 删除行：同样使用 drop 方法。
df_dropped = df.drop(0)        # 删除索引为 0 的行
print(df_dropped)
print('-'* 30)





""" DataFrame 的统计分析 """
# 描述性统计：使用 .describe() 查看数值列的统计摘要。
print(df.describe())

# 计算统计数据：使用聚合函数如 .sum()、.mean()、.max() 等。
print(df['Age'].sum())
print(df['Age'].mean())
print('-'* 30)





""" DataFrame 的索引操作  """
# 重置索引- 使用 .reset_index()
df_reset = df.reset_index(drop=True)
print(df_reset)
print('-'* 30)


# 设置索引-使用 .set_index()
df_set = df.set_index('Name')
print(df_set)



""" DataFrame 的布尔索引 """
# 使用布尔表达式：根据条件过滤 DataFrame。
print(df[df['Age'] > 12])


""" DataFrame 的数据类型
查看数据类型：使用 dtypes 属性。 """
print(df.dtypes)
# 转换数据类型：使用 astype 方法。
df['B'] = df['B'].fillna(0)    # 用0填充NaN值
df['B'] = df['B'].astype('int64')  # 转换为int64类型
print(df["B"])
print('-'* 30)



""" 
    DataFrame 的合并与分割-合并：使用 concat 或 merge 方法。
 """
df1 = pd.DataFrame(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), columns=["Name", 1, 2])
print(df1)

s1 = pd.Series(['Alice', 'Bob', 'Charlie'])
s2 = pd.Series([25, 30, 35])
s3 = pd.Series(['New York', 'Los Angeles', 'Chicago'])
df2 = pd.DataFrame({'Name': s1, 'Age': s2, 'City': s3})
# 纵向合并
df3 = pd.concat([df1, df2], ignore_index=True)
print(df3)

# 横向合并-内连接（inner join）- 数据类型需保持一致
df1['Name'] = df1['Name'].astype(str)
df2['Name'] = df2['Name'].astype(str)
df4 = pd.merge(df1, df2, on="Name")

# 分割：使用 pivot、melt 或自定义函数。
df_pivot = df.pivot(index='Name', columns='Age', values='Name')   # 长格式转宽格式
print(df_pivot)

# 宽格式转长格式
df_melt = df.melt(id_vars='Name', value_vars=['Age', 'Name'])
print(df_melt)



"""
    索引和切片
    DataFrame 支持对行和列进行索引和切片操作。
"""
print(df[["Name", "Age"]])      # 提取多列
print(df[1:3])      # 切片行
print(df.loc[:, "Name"])        # 提取单列
print(df.loc[1:2], ["Name", "Age"])     # 标签索引提取指定行列
print(df.iloc[:, 1:])       # 位置索引提取指定列